﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NextAction.Domain.Abstract;
using NextAction.Domain.Entities;
using System.Linq.Expressions;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Data.Entity;

namespace NextAction.Domain.Concrete
{
    public class EFGtdEntityRepository : IGtdEnityRepository
    {
        private EFDbContext context;
        private int UserID = 0;

        public EFGtdEntityRepository()
        {
            context = new EFDbContext();
        }

        private IQueryable<GtdTask> GtdTasks
        {
            get { return context.GtdTasks; }
        }

        public void SaveGtdTask(GtdTask task)
        {
            if (task.TaskID == 0)
            {
                context.GtdTasks.Add(task);
            }
            else
            {
                var updatedTask = GetGtdTask(task.TaskID);
                updatedTask.CopyFrom(task);
            }

            context.SaveChanges();
        }

        public void DeleteGtdTask(GtdTask task)
        {
            context.GtdTasks.Remove(task);
            context.SaveChanges();
        }

        public IQueryable<GtdTask> GetGtdTasks(GtdTaskType taskType)
        {
            return GetGtdTasks(taskType, ScheduleFilter.NotScheduled);
        }

        public Expression<Func<GtdTask, bool>> CreateWhereExp(GtdTaskType taskType, ScheduleFilter schedule)
        {
            Expression<Func<GtdTask, bool>> whereExp;
            int taskTypeID = (int)taskType;
            var tomorrow = DateTime.Today.AddDays(1);
            var afterTomorrow = DateTime.Today.AddDays(2);
            switch (schedule)
            {
                case ScheduleFilter.Today:
                    whereExp = p => p.TaskTypeID == taskTypeID && p.IsCompleted == false 
                        && p.ScheduleDate < tomorrow;
                    break;
                case ScheduleFilter.Tomorrow:
                    whereExp = p => p.TaskTypeID == taskTypeID && p.IsCompleted == false
                        && p.ScheduleDate >= tomorrow && p.ScheduleDate < afterTomorrow;
                    break;
                case ScheduleFilter.Scheduled:
                    whereExp = p => p.TaskTypeID == taskTypeID && p.IsCompleted == false
                        && p.ScheduleDate != null;
                    break;
                default:
                    whereExp = p => p.TaskTypeID == taskTypeID && p.IsCompleted == false 
                        && p.ScheduleDate == null;
                    break;
            }

            return whereExp;
        }

        public IQueryable<GtdTask> GetGtdTasks(GtdTaskType taskType, ScheduleFilter schedule)
        {
            var projects = GetProjectsOrderedByName().ToDictionary(x => x.ProjectID);
            projects.Add(0, new GtdProject());

            var tasks = GtdTasks.Where(CreateWhereExp(taskType, schedule)).OrderByDescending(p => p.PriorityID).ThenBy(p => p.Name);

            foreach (GtdTask task in tasks)
            {
                task.ProjectName = projects[task.ProjectID].Name;
            }

            return tasks;
        }

        public GtdTask GetGtdTask(int taskID)
        {
            return GtdTasks.First(p => p.TaskID == taskID);
        }

        public IList<GtdTask> GetTasksForProject(int projectID)
        {
            return context.GetTasksForProject(projectID).ToList();
        }

        public IQueryable<Priority> GetPriorities()
        {
            return context.Priorities; 
        }

        public BucketSizes GetBucketSizes()
        {
            var sizes = new BucketSizes();
            var today = DateTime.Today;
            var tomorrow = today.AddDays(1);

            sizes.Add(
                BucketType.Inbox,
                context.GetTasksCount(UserID, GtdTaskType.Inbox, null, null, null, null)
                );

            sizes.Add(
                BucketType.Next,
                context.GetTasksCount(UserID, GtdTaskType.Next, null, null, null, true)
                );

            sizes.Add(
                BucketType.Today,
                context.GetTasksCount(UserID, GtdTaskType.Next, null, today, null, null)
                );

            sizes.Add(
                BucketType.Tomorrow,
                context.GetTasksCount(UserID, GtdTaskType.Next, tomorrow, tomorrow, null, null)
                );

            sizes.Add(
                BucketType.Scheduled,
                context.GetTasksCount(UserID, GtdTaskType.Next, null, null, true, null)
                );

            sizes.Add(
                BucketType.Wait,
                context.GetTasksCount(UserID, GtdTaskType.Wait, null, null, null, null)
                );

            sizes.Add(
                BucketType.Someday,
                context.GetTasksCount(UserID, GtdTaskType.Someday, null, null, null, null)
                );

            return sizes;
        }

        public IList<GtdProject> GetProjectsOrderedByName()
        {
            return context.GetProjects(UserID, false, false, true).ToList();
        }

        public IList<GtdProject> GetProjectsOrderedByPriority()
        {
            return context.GetProjects(UserID, false, false, true).ToList();
        }

        public GtdProject GetProject(int projectID)
        {
            return context.GetProject(projectID);
        }

        public void SaveGtdProject(GtdProject project)
        {
            if (project.ProjectID == 0)
            {
                context.GtdProjects.Add(project);
            }

            context.SaveChanges();
        }

        public void DeleteGtdProject(GtdProject project)
        {
            context.GtdProjects.Remove(project);
            context.SaveChanges();
        }

        public IList<GtdProject> GetGtdProjectsWithDefault()
        {
            var projects = new List<GtdProject>() { GtdProject.Default() };
            projects.AddRange(GetProjectsOrderedByName());

            return projects;
        }

        private DbSqlQuery<GtdProject> GetProjects()
        {
            return context.GetProjects(UserID, false, false, false);
        }
    }
}
